﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AliyunOpenSearch4Net
{
    internal static partial class Extensions
    {
        private static readonly DateTime _initialTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        internal static double ToUnixTicks(this DateTime dt)
        {
            DateTime d2 = dt.ToUniversalTime();
            TimeSpan ts = d2.Subtract(_initialTime);
            return ts.TotalMilliseconds;
        }

        internal static DateTime FromUnixTicksToTime(this long ticks)
        {
            var dt = _initialTime.AddMilliseconds((double)ticks);
            return dt.ToLocalTime();
        }

        internal static void Add(this IList<KeyValuePair<string, string>> list, string key, string value)
        {
            list.Add(new KeyValuePair<string, string>(key, value));
        }

        internal static string ToQueryString(this List<KeyValuePair<string, string>> list)
        {
            if (list == null || list.Count == 0)
                return string.Empty;

            var arr = list.OrderBy(p => p.Key)
                .Select(p => p.Key.CanonicalizedString() + "=" + p.Value.CanonicalizedString())
                .ToArray();

            return string.Join("&", arr);
        }

        internal static string CanonicalizedString(this string source)
        {
            var sb = new StringBuilder();

            foreach (var c in source)
            {
                if (char.IsDigit(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-' || c == '.' || c == '~')
                {
                    sb.Append(c);
                    continue;
                }

                var charValue = (int)c;

                if (charValue > 255)
                {
                    foreach (var b in Encoding.UTF8.GetBytes(c.ToString()))
                    {
                        sb.Append("%");
                        sb.Append(b.ToString("X"));
                    }

                    continue;
                }

                sb.Append("%");
                sb.Append(((int)c).ToString("X"));
            }

            return sb.ToString();
        }

        internal static string SortAndEncodeQuery(string query)
        {
            if (string.IsNullOrEmpty(query))
                return string.Empty;

            const string doubleAnd = "&&";
            const string doubleAndReplacement = "[*]";

            query = query.Replace(doubleAnd, doubleAndReplacement);

            var items = query.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(v =>
                {
                    var arr = v.Split(new char[] { '=' }, 2);
                    return new KeyValuePair<string, string>(CanonicalizedString(arr[0]), CanonicalizedString(arr[1].Replace(doubleAndReplacement, doubleAnd)));
                }).OrderBy(p => p.Key)
            .Select(p => p.Key + "=" + p.Value)
            .ToArray();

            return string.Join("&", items);
        }
    }
}
